package thread.n8;

import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantReadWriteLock;

/**
 * 读写锁的简单使用。
 */
public class ReentrantReadWriteLockTest {
    public static void main(String[] args) throws InterruptedException {
//        test1();
//        test2();
//        test4();
        test5();
    }


    //读读并发。
    private static void test1(){
        DataContainer dataContainer = new DataContainer();

        new Thread(() -> {
            try {
                dataContainer.read();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        new Thread(() -> {
            try {
                dataContainer.read();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t2").start();
    }

    //读写/写写 互斥。
    private static void test2() throws InterruptedException {
        DataContainer dataContainer = new DataContainer();

        new Thread(() -> {
            try {
                dataContainer.read();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "t1").start();

        Thread.sleep(100);
        new Thread(() -> {
            dataContainer.write();
        }, "t2").start();
    }

    //特性1：读锁不支持条件变量。写锁支持。
    private static void test3(){
        ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
        //读锁，获取condition，会报【UnsupportedOperationException】异常。
//        Condition condition = rw.readLock().newCondition();
        //写锁,可以获取条件变量.
        Condition condition = rw.writeLock().newCondition();
    }

    //特性2:重入时不支持锁的升级, 即【读锁不支持升级为写锁】
    private static void test4(){
        ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
        //获取读锁。
        rw.readLock().lock();
        System.out.println("获取读锁成功....");
        //之后,获取写锁。
        //这一步,会出现死锁,导致程序锁死.
        rw.writeLock().lock();
        System.out.println("获取写锁成功...");
        rw.writeLock().unlock();
        System.out.println("释放写锁成功...");
        rw.readLock().unlock();
        System.out.println("释放读锁成功...");
    }

    //特性3:重入时支持锁的降级, 即【写锁可以降级为读锁】
    private static void test5(){
        ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
        //获取写锁。
        rw.writeLock().lock();
        System.out.println("获取写锁成功....");
        //之后,获取读锁。
        //这一步,会出现死锁,导致程序锁死.
        rw.readLock().lock();
        System.out.println("获取读锁成功...");
        rw.readLock().unlock();
        System.out.println("释放读锁成功...");
        rw.writeLock().unlock();
        System.out.println("释放写锁成功...");
    }
}


/**
 * 一个读写锁的容器：
 *  read方法使用读锁，
 *  write方法使用写锁。
 */
class DataContainer {
    //保护的对象。
    private Object data;
    //new一个读写锁。
    private ReentrantReadWriteLock rw = new ReentrantReadWriteLock();
    //获取它的读锁
    private ReentrantReadWriteLock.ReadLock r = rw.readLock();
    //获取它的写锁。
    private ReentrantReadWriteLock.WriteLock w = rw.writeLock();

    public Object read() throws InterruptedException {
        System.out.println("获取读锁...");
        r.lock();
        try {
            System.out.println("读取");
            Thread.sleep(1000);
            return data;
        } finally {
            System.out.println("释放读锁...");
            r.unlock();
        }
    }

    public void write() {
        System.out.println("获取写锁...");
        w.lock();
        try {
            System.out.println("写入");
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        } finally {
            System.out.println("释放写锁...");
            w.unlock();
        }
    }
}


/**
 * 读写锁的升级与降级.
 */
class CachedData {
    Object data;
    // 是否有效，如果失效，需要重新计算 data
    volatile boolean cacheValid;
    final ReentrantReadWriteLock rwl = new ReentrantReadWriteLock();
    void processCachedData() {
        //获取读锁
        rwl.readLock().lock();
        if (!cacheValid) {
            // 获取写锁前必须释放读锁,因为重入不支持 锁的升级
            rwl.readLock().unlock();
            //获取写锁
            rwl.writeLock().lock();
            try {
                // 判断是否有其它线程已经获取了写锁、更新了缓存, 避免重复更新
                if (!cacheValid) {
                    data = null;
                    cacheValid = true;
                }
                // 降级为读锁, 释放写锁, 这样能够让其它线程读取缓存
                rwl.readLock().lock();
            } finally {
                //释放写锁.
                rwl.writeLock().unlock();
            }
        }
        // 自己用完数据, 释放读锁
        try {
//            use(data);
        } finally {
            //最后,释放读锁.
            rwl.readLock().unlock();
        }
    }
}